package net.takela.common.webflux.security.service;


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import net.takela.common.utils.AesUtils;
import net.takela.common.utils.JSONUtils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Type;
import java.util.Date;

public class AuthTokenManager {
    //token有效时长
    private Long expiration;
    //编码秘钥
    private String key;
    private String userInfoKey;
    private String header;
    private String headerPrefix;
    private String cookieName;
    private String issuer;
    private String audience;

    private String userClaimKey = "userInfo";
    private String versionClaimKey = "version";
    private String encryptVersion = "AA";
    private static int CHECK_CODE_LEN = 8;
    private static int VER_LEN = 2;
    private static SecretKey secretKey;
    private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    /**
     * 
     */
    public Long getExpiration() {
        return expiration;
    }
    /**
     * 
     */
    public void setExpiration(Long expiration) {
        this.expiration = expiration;
    }
    /**
     * 
     */
    public String getKey() {
        return key;
    }
    /**
     * 
     */
    public void setKey(String key) {
        this.key = key;
    }
    /**
     * 
     */
    public String getHeader() {
        return header;
    }
    /**
     * 
     */
    public void setHeader(String header) {
        this.header = header;
    }
    /**
     * 
     */
    public String getHeaderPrefix() {
        return headerPrefix;
    }
    /**
     * 
     */
    public void setHeaderPrefix(String headerPrefix) {
        this.headerPrefix = headerPrefix;
    }
    /**
     * 
     */
    public String getCookieName() {
        return cookieName;
    }
    /**
     * 
     */
    public void setCookieName(String cookieName) {
        this.cookieName = cookieName;
    }
    /**
     * 
     */
    public String getIssuer() {
        return issuer;
    }
    /**
     * 
     */
    public void setIssuer(String issuer) {
        this.issuer = issuer;
    }
    /**
     * 
     */
    public String getAudience() {
        return audience;
    }
    /**
     * 
     */
    public void setAudience(String audience) {
        this.audience = audience;
    }
    /**
     * 
     */
    public String getUserClaimKey() {
        return userClaimKey;
    }
    /**
     * 
     */
    public void setUserClaimKey(String userClaimKey) {
        this.userClaimKey = userClaimKey;
    }
    /**
     * 
     */
    public String getUserInfoKey() {
        return userInfoKey;
    }
    /**
     * 
     */
    public void setUserInfoKey(String userInfoKey) {
        this.userInfoKey = userInfoKey;
    }
    /**
     * 
     */
    public String getVersionClaimKey() {
        return versionClaimKey;
    }
    /**
     * 
     */
    public void setVersionClaimKey(String versionClaimKey) {
        this.versionClaimKey = versionClaimKey;
    }
    /**
     * 
     */
    public String getEncryptVersion() {
        return encryptVersion;
    }
    /**
     * 
     */
    public void setEncryptVersion(String encryptVersion) {
        this.encryptVersion = encryptVersion;
    }
    /**
     * 
     */
    public SecretKey getSecretKey() {
        if (secretKey == null){
            synchronized (this){
                if (secretKey == null){
                    byte[] encodedKey = Decoders.BASE64.decode(key);
                    secretKey = new SecretKeySpec(encodedKey, signatureAlgorithm.getJcaName());
                }
            }
        }
        return secretKey;
    }

    /**
     * 添加token 壳，增加复杂性
     * @param token
     * @return
     */
    private String addTokenShell(String token){
        if (StringUtils.isNotEmpty(encryptVersion)){
            String checkCode = DigestUtils.sha256Hex(token).substring(0, CHECK_CODE_LEN);
            return encryptVersion + token+ checkCode;
        }
        return token;
    }
    private String removeTokenShell(String token){
        if (StringUtils.isEmpty(encryptVersion)){
            return token;
        }
        String checkCode = token.substring(token.length() - CHECK_CODE_LEN);
        String encryptVer = token.substring(0, VER_LEN);
        String t = token.substring(VER_LEN, token.length() - CHECK_CODE_LEN);
        String checkCode2 = DigestUtils.sha256Hex(t).substring(0, CHECK_CODE_LEN);
        if (checkCode.equalsIgnoreCase(checkCode2)){
            return t;
        }
        return token;
    }
    //1 使用jwt根据用户名生成token
    /**
     * 
     */
    public String createJwtToken(String uid, Object payload) {
        String payloadString = JSONUtils.toString(payload);
        JWTCreator.Builder builder = JWT.create().withIssuer(issuer)
                .withSubject(uid)
                .withAudience(new String[]{audience})
                .withClaim(userClaimKey, payloadString)
                .withExpiresAt(new Date(System.currentTimeMillis() + expiration*1000));
        String token = builder.sign(Algorithm.HMAC256(key));
        return addTokenShell(token);
    }
    //2 根据token字符串得到用户信息
    /**
     * 
     */
    public Claims parseJwts(String token) {
        Jws<Claims> jws = Jwts.parser()
//                .setSigningKey(getSecretKey())
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token);

        Claims claims = jws.getBody();
        return claims;
    }
    private String parseTokenFromHeader(ServerHttpRequest request){
        String authToken = request.getHeaders().getFirst(header);
        if (authToken != null && authToken.startsWith(headerPrefix)) {
            authToken = authToken.substring(headerPrefix.length()); // The part after "Bearer "
        }
        return authToken;
    }
    private String parseTokenFromCookie(ServerHttpRequest request){
        if (!StringUtils.isEmpty(cookieName) && request.getCookies() != null) {
            HttpCookie cookie = request.getCookies().getFirst(cookieName);
            if (cookie != null){
                return cookie.getValue();
            }
        }
        return null;
    }
    /**
     * 
     */
    public  <T> T parseUserInfoFromRequest(ServerHttpRequest request, Type payloadType){
        String token = parseTokenFromHeader(request);
        if (StringUtils.isEmpty(token)){
            token = parseTokenFromCookie(request);
        }
        if ( !StringUtils.isEmpty(token) ) {
            String t = removeTokenShell(token);
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(key)).build();
            DecodedJWT d = jwtVerifier.verify(t);
            Claim userInfoClaim = d.getClaim(userClaimKey);
            if (userInfoClaim == null){
                return null;
            }
            String userInfoString = userInfoClaim.asString();
            Claim versionClaim = d.getClaim(versionClaimKey);
            if (versionClaim != null && !versionClaim.isMissing() && versionClaim.asInt() >= 3000) {
                userInfoString = AesUtils.decryptString(Base64.decodeBase64(userInfoString), userInfoKey);
            }
            T payload = JSONUtils.parse(userInfoString, payloadType);
            return payload;
        }
        return null;
    }
    //3 删除token
    /**
     * 
     */
    public void removeToken(String token) { }
}
